﻿using System;

namespace AlgorytmyGenetyczne.Kodowania
{
    public class KodowanieBinarne : AbstractKodowanie
    {
        public KodowanieBinarne(int dokladnosc, double poczatekDziedziny, double koniecDziedziny)
        {
            Dokladnosc = dokladnosc;
            PoczatekDziedziny = poczatekDziedziny;
            KoniecDziedziny = koniecDziedziny;
            DlugoscDziedziny = dlugoscDziedziny();
            wyznaczIloscBitow();
        }

        private double dlugoscDziedziny()
        {
            return KoniecDziedziny - PoczatekDziedziny;
        }

        private long znajdzDziesietnaWartoscReprezentacjiBinarnej(double liczbaDoZamiany)
        {
            double czynnik1 = (-PoczatekDziedziny + liczbaDoZamiany);
            double czynnik2 = (Math.Pow(2.0, IloscBitow) - 1);

            double wynik = (czynnik1 * czynnik2) / DlugoscDziedziny;
            long wynikLong = (long)Math.Round(wynik);
            return wynikLong;
        }


        private long znajdzDziesietnaWartoscReprezentacjiBinarnej(bool[] reprezentacjaBinarna)
        {
            long suma = 0;

            for (int potega = 0; potega < IloscBitow; potega++)
            {
                if (reprezentacjaBinarna[potega])
                {
                    suma += (long)Math.Round(Math.Pow(2.0, potega));
                }
            }
            return suma;
        }

        private bool[] utworzCiagBinarny(long dziesietnaWartoscReprezentacji)
        {
            bool[] reprezentacjaBinarna = new bool[IloscBitow];
            long wartosc = dziesietnaWartoscReprezentacji;

            int i = 0;
            while (wartosc > 0)
            {
                reprezentacjaBinarna[i] = wartosc % 2 == 1;
                i++;
                wartosc /= 2;
            }

            return reprezentacjaBinarna;
        }

        private double wyznaczLiczbeZmiennoprzecinkowa(long dziesietnaWartoscReprezentacji)
        {
            double czynnik1 = DlugoscDziedziny * dziesietnaWartoscReprezentacji;
            double czynnik2 = (Math.Pow(2.0, IloscBitow) - 1);

            double wynik = PoczatekDziedziny + (czynnik1 / czynnik2);
            return wynik;
        }

        public int wyznaczIloscBitow()
        {
            double iloscPodprzedialow = dlugoscDziedziny() * Math.Pow(10, Dokladnosc);
            IloscBitow = 1;
            double dwojkaWPotedze = 2.0;

            while (dwojkaWPotedze - 1.0 < iloscPodprzedialow)
            {
                IloscBitow++;
                dwojkaWPotedze *= 2.0;
            }
            return IloscBitow;
        }

        public override Genotyp koduj(object fenotyp)
        {
            double wartoscFenotypu = (double) fenotyp;
            if(wartoscFenotypu > KoniecDziedziny || wartoscFenotypu < PoczatekDziedziny)
            {
                throw new ArgumentException("Podana liczba: "+wartoscFenotypu+" nie nalezy do dziedziny ["+PoczatekDziedziny+","+KoniecDziedziny+"]");
            }
            long dziesietnaReprezentacja = znajdzDziesietnaWartoscReprezentacjiBinarnej((double)fenotyp);
            Genotyp genotyp = new Genotyp();
            genotyp.Geny = utworzCiagBinarny(dziesietnaReprezentacja);
            return genotyp;
        }

        public override object dekoduj(Genotyp genotyp)
        {
            if (genotyp.Geny.Length != IloscBitow)
            {
                throw new ArgumentException("Niepoprawna dlugosc genotypu rowna " + genotyp.Geny.Length + ". Ten obiekt obsluguje dlugosc: " + IloscBitow);
            }

            long dziesietnaReprezentacja = znajdzDziesietnaWartoscReprezentacjiBinarnej(genotyp.Geny);
            return wyznaczLiczbeZmiennoprzecinkowa(dziesietnaReprezentacja);
        }

        public override string ToString()
        {
            return "Binarne";
        }
    }
}
